﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace iStudio.Engine
{
    public class BlockFileChannel
    {
        BlockFile _blockFile;
        int _channelIndex;
        float[] _storage;

        public BlockFile Source
        {
            get { return _blockFile; }
        }

        public int ChannelIndex
        {
            get { return _channelIndex; }
        }

        public long Position
        {
            get { return Source.Position; }
            set { Source.Position = value; }
        }

        public long Length
        {
            get { return Source.Position; }
        }

        public BlockFileChannel(BlockFile blockFile, int channelIndex)
        {
            if (blockFile == null)
                throw new ArgumentNullException("blockFile");
            if (channelIndex < 0 || channelIndex >= blockFile.Channels)
                throw new ArgumentOutOfRangeException("channelIndex");

            _blockFile = blockFile;
            _channelIndex = channelIndex;
        }

        public int Read(float[] buffer, int offset, int count)
        {
            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (buffer.Length < count)
                throw new ArgumentException("length", "buffer");
            if (count <= 0)
                throw new ArgumentOutOfRangeException("count");
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset");

            int samplesToRead = count * Source.Channels;
            _storage = _storage.CheckForSize(samplesToRead);
            int read = Source.Read(_storage, 0, samplesToRead);
            read = Math.Max(0, read - (read % Source.Channels));

            unsafe
            {
                fixed (float* psrc = &_storage[0], pdest = &buffer[0])
                {
                    float* ppsrc = psrc + ChannelIndex;
                    float* ppdest = pdest + offset;

                    int processed = 0;
                    while (processed < count && processed < (read / Source.Channels))
                    {
                        *(ppdest) = *(ppsrc);
                        ppdest++;
                        ppsrc += Source.Channels;
                        processed++;
                    }

                    return processed;
                }
            }
        }

        public void Write(float[] buffer, int offset, int count)
        {
            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (buffer.Length < count)
                throw new ArgumentException("length", "buffer");
            if (count <= 0)
                throw new ArgumentOutOfRangeException("count");
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset");

            int storageoffset = 0;
            if (Position < Length - 1)
            {
                long pos = Source.Position;

                int samplesToRead = Math.Min((int)(Length - Position) * Source.Channels, count * 4);
                _storage = _storage.CheckForSize(count * 4); //anyway we have to write so much to the file
                int read = Source.Read(_storage, 0, samplesToRead);
                if (read != samplesToRead)
                    throw new InvalidOperationException(); //todo: handle exception
                storageoffset = read;

                Source.Position = pos;
            }
            else
            {
                _storage = _storage.CheckForSize(count * 4);
            }

            if(_storage.Length - storageoffset > 0)
                Array.Clear(_storage, storageoffset, _storage.Length - storageoffset);

            unsafe
            {
                fixed (float* psrc = &buffer[0], pdest = &_storage[0])
                {
                    float* ppsrc = psrc + offset;
                    float* ppdest = pdest + ChannelIndex;

                    int processed = 0;
                    while (processed < count)
                    {
                        *(ppdest) = *(ppsrc);
                        ppdest += Source.Channels;
                        ppsrc++;
                        processed++;
                    }
                    System.Diagnostics.Debug.Assert(count == processed);

                    Source.Write(_storage, 0, processed * Source.Channels);
                }
            }
        }
    }
}
